#!/usr/bin/env python
# -*- coding: utf-8 -*-

import asyncore
import traceback
import socket, time
import StringIO
import mimetools, urlparse

class CloseConnection(Exception):
    pass

class async_client(asyncore.dispatcher_with_send):

    def __init__(self, host, port, path, consumer):
        asyncore.dispatcher_with_send.__init__(self)

        self.host = host
        self.port = port
        self.path = path
        self.consumer = consumer
        self.status = None
        self.header = None

        self.bytes_in = 0
        self.bytes_out = 0

        self.data = ""

        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.connect((host, port))

    def handle_connect(self):
        text = "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n" % (self.path, self.host)
        print text
        self.send(text)
        self.bytes_out = self.bytes_out + len(text)

    def handle_expt(self):
        self.close() # connection failed, when out-of-band data arrives

    def handle_read(self):
        data = self.recv(2048)
        self.bytes_in = self.bytes_in + len(data)

        if not self.header:
            self.data = self.data + data

            header = self.data.split("\r\n\r\n", 1)
            print header
            if len(header) <= 1:
                return
            header, data = header

            fp = StringIO.StringIO(header)
            self.status = fp.readline().split(" ", 2)
            print "status:", self.status
            self.header = mimetools.Message(fp)
            print "header: ", self.header
            self.data = ""
            try:
                self.consumer.http_header(self)
            except CloseConnection:
                self.close()
                return

            if not self.connected:
                return

        if data:
            self.consumer.feed(data)

    def handle_error(self):
        traceback.print_exc()
        self.close()

    def handle_close(self):
        #socket is closed or reset
        self.consumer.close()
        self.close()

def do_request(uri, consumer):

    scheme, host, path, params, query, fragment = urlparse.urlparse(uri)
    assert scheme == "http", "only supports HTTP requests"
    try:
        host, port = host.split(":", 1)
        port = int(port)
    except (TypeError, ValueError):
        port = 80
    if not path:
        path = "/"
    if params:
        path = path + ";" + params
    if query:
        path = path + "?" + query

    return async_client(host, port, path, consumer)

class connection_manager:
    max_connections = 3
    max_size = 1000000 # bytes

    def __init__(self):
        self._queue = []

    def request(self, uri, consumer):
        self._queue.append((uri, consumer))

    def poll(self, timeout=0.1):
        while self._queue and len(asyncore.socket_map) < self.max_connections:
            do_request(*self._queue.pop(0))
        now = time.time()
        for channel in asyncore.socket_map.values():
            if channel.bytes_in > self.max_size:
                channel.close()
        # keep the network running
        asyncore.poll(timeout)
        # return non-zero if we should keep polling
        return len(self._queue) or len(asyncore.socket_map)


class consumer:
    def http_header(self, client):
        if (client.status[1] != "200"): # or client.header["content-type"] != "text/xml"): no one is perfect
            print client.host, "failed"
            print client.header["content-type"]
            raise CloseConnection
        self.host = client.host
        self.path = client.path
        self.file = None

    def http_failed(self, client):
        pass

    def feed(self, data):
        if self.file is None:
            self.file = open(self.host + ".rss", "w")
        self.file.write(data)

    def close(self):
        if self.file is not None:
            print self.host + ".rss ok"
            self.file.close()
        self.file = None